﻿namespace NTLib.Core.Pool
{
    using Extensions;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.CompilerServices;

    /// <summary>
    /// Pool implementation to managed only the creation of T's object 
    /// </summary>
    /// <typeparam name="T">Concrete object's type of the object must store</typeparam>
    public class ObjectPool<T> : BasePool
        where T : IPoolItem, new()
    {
        #region Fields

        private readonly Queue<T> _freeObjects;

        #endregion

        #region Ctor

        /// <summary>
        /// Initializes a new instance of the <see cref="ObjectPool{T}"/> class.
        /// </summary>
        public ObjectPool()
        {
            this._freeObjects = new Queue<T>();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the free objects count.
        /// </summary>
        /// <value>
        /// The free objects count.
        /// </value>
        public uint FreeObjectsCount
        {
            get
            {
                lock (this._syncPoolObject)
                {
                    return (uint)this._freeObjects.Count;
                }
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets an object T from this pool.
        /// </summary>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public T Get()
        {
            return base.Get<T>();
        }

        /// <summary>
        /// Cleans all.
        /// </summary>
        /// <returns>
        /// Return all the items in the recycle storage to be destroy
        /// </returns>
        protected override ICollection<IPoolItem> CleanAll()
        {
            var minItems = MinItems;
            if (this._freeObjects.Count < minItems)
            {
                for (int i = this._freeObjects.Count; i < minItems; ++i)
                    this._freeObjects.Enqueue(new T());
            }

            var maxitem = MaxItems;

            List<IPoolItem> toDestroy = null;
            if (this._freeObjects.Count > maxitem)
            {
                toDestroy = new List<IPoolItem>();
                for (int i = this._freeObjects.Count; i > maxitem; --i)
                    toDestroy.Add(this._freeObjects.Dequeue());
            }
            return toDestroy;
        }

        /// <summary>
        /// Gets the item from recycle.
        /// </summary>
        /// <typeparam name="U">The concret type to get</typeparam>
        /// <returns>The object from the recycle queue</returns>
        protected override IPoolItem GetItemFromRecycle<U>()
        {
            return GetItemFromRecycle(null);
        }

        /// <summary>
        /// Gets item from the recycle storage
        /// </summary>
        /// <param name="type"></param>
        /// <returns>
        ///   <c>T Instance</c> if the recycle container contains one; otherwise <c>null</c>
        /// </returns>
        protected override IPoolItem GetItemFromRecycle(Type type)
        {
            if (this._freeObjects.Count > 0)
                return this._freeObjects.Dequeue();

            return null;
        }

        /// <summary>
        /// Removes the item from recycle storage.
        /// </summary>
        /// <param name="item">The item to remove from the recycle storage</param>
        protected override void RemoveItemFromRecycle(IPoolItem item)
        {
            if (this._freeObjects.Contains((T)item))
            {
                var safeItems = this._freeObjects.Where(el => (IPoolItem)el != item).ToArray();
                this._freeObjects.SafeClear();

                foreach (var it in safeItems)
                    this._freeObjects.Enqueue(it);
            }
        }

        /// <summary>
        /// Sets the item to the recycle storage.
        /// </summary>
        /// <param name="item">The item to recycle.</param>
        /// <returns>
        ///   <c>True</c> if the item have been recycle; otherwise false;
        /// </returns>
        protected override bool SetToTheRecycleStorage(IPoolItem item)
        {
            if (base.MaxItems > this._freeObjects.Count)
            {
                this._freeObjects.Enqueue((T)item);
                return true;
            }
            return false;
        }

        #endregion
    }
}
